package tsb.sax;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Vector;
import tsb.dtd.*;
import tsb.util.*;
import InterfazGrafica.Principal;

/**
 * El parser sax propiamente dicho
 * controla el flujo de datos desde los caracteres hacia los cabeceras y las validaciones dtd
 */
public class SAXParser {
    private ContentHandlerModel contentH;
    private ErrorHandlerModel errorH;
    private File xmlFile;
    private Clasificador clasificador;
    private XMLReader xmlReader;
    private ControladorEquilibrio equilibrio;
    private DTDValidador dtdValidador;
    private Principal principal;

    public SAXParser(Principal pPrincipal) {
        principal = pPrincipal;
        this.clasificador = new Clasificador();
        this.equilibrio   = new ControladorEquilibrio();
    }

    public ContentHandlerModel getContentHandler() {
        return contentH;
    }

    public void setContentHandler(ContentHandlerModel contentH) {
        this.contentH = contentH;
    }

    public ErrorHandlerModel getErrorHandler() {
        return errorH;
    }

    public void setErrorHandler(ErrorHandlerModel errorH) {
        this.errorH = errorH;
    }

    public File getXmlFile() {
        return xmlFile;
    }

    public void setXmlFile(File xmlFile) {
        this.xmlFile = xmlFile;
    }

    public void doParse() throws FileNotFoundException {
        this.equilibrio.clear();

        try {
            String tagLeido;
            this.xmlReader = new XMLReader(xmlFile);
            this.inicioDocumento();

            do {
                tagLeido = xmlReader.getNext();
                if (tagLeido != null) {
                    clasificarTags(tagLeido); //empieza a identificar la estructura del xml
                }
            } while (tagLeido != null);

            if (equilibrio.esValido()) {
                this.finDocumento();
            }
            else {
                throw new SAXException(SAXException.ERROR_FATAL, equilibrio.getCausaError());
            }

        } catch (SAXException ex) {
            clasificarException(ex);
        } finally {
            if (xmlReader != null) {
                xmlReader.close();
            }
        }
    }

    /* ****************************************
     * 
     * METODOS PRIVADOS
     * 
     * ****************************************/
    /*
        metodos para el content handler
     */
    private void clasificarTags(String tagLeido) throws SAXException {
        int tipoTag = this.clasificador.clasificar(tagLeido);
        int i = 0;

        if (contentH != null) {
            if (tipoTag == Clasificador.INVALIDO) {
                throw new SAXException(SAXException.ERROR_FATAL, "Sintaxis Inválida: " + tagLeido);
            }
            else if (tipoTag == Clasificador.TAG_INICIO) {
                principal.agregarTag(tagLeido, Principal.tipoTAG.INICIO);
                this.tagInicio(tagLeido);
            }
            else if (tipoTag == Clasificador.TAG_FINAL) {
                principal.agregarTag(tagLeido, Principal.tipoTAG.FIN);
                this.tagFinal(tagLeido);
            }
            else if (tipoTag == Clasificador.TAG_AUTOCONTENIDO) {
                principal.agregarTag(tagLeido, Principal.tipoTAG.PROLOGO);
                this.tagAutoContenido(tagLeido);
            }
            else if (tipoTag == Clasificador.CONTENIDO) {
                principal.agregarTag(tagLeido, Principal.tipoTAG.PROLOGO);
                this.addContenido(tagLeido);
            } 
            else if (tipoTag == Clasificador.CONTENIDO_CDATA){
                principal.agregarTag(tagLeido, Principal.tipoTAG.PROLOGO);
                this.addContenido(tagLeido);
            }
            else if (tipoTag == Clasificador.XML_PROLOGO) {
                principal.agregarTag(tagLeido, Principal.tipoTAG.PROLOGO);
                this.setXMLPrologo(tagLeido);
            }
            else if (tipoTag == Clasificador.DTD_INFO) {
                principal.agregarTag(tagLeido, Principal.tipoTAG.PROLOGO);
                try {
                    //le paso el lector al dtd
                    DTDParser dtdParser = new DTDParser(this.xmlReader);
                    //le pido el validador
                    this.dtdValidador = dtdParser.getDtdValidador();
                } catch (DTDException ex) {
                    this.clasificarException(new SAXException(SAXException.ERROR, "DTD ERROR: "+ex.getMessage()));
                }//try-cath
            }//else if
        }
    }

    private void inicioDocumento() {
        try {
            this.contentH.inicioDocumento();
            if (this.dtdValidador != null) {
                this.dtdValidador.validarInicioDocumento();
            }
        } catch (DTDException ex) {
            this.clasificarException(new SAXException(SAXException.ERROR, ex.getMessage()));
        } catch (SAXException ex) {
            this.clasificarException(ex);
        }
    }

    private void finDocumento() {
        try {
            this.contentH.finDocumento();
            if (this.dtdValidador != null) {
                this.dtdValidador.validarFinalDocumento();
            }
        } catch (DTDException ex) {
            this.clasificarException(new SAXException(SAXException.ERROR, ex.getMessage()));
        } catch (SAXException ex) {
            this.clasificarException(ex);
        }
    }

    private void addContenido(String tagLeido) {
        try {
            this.contentH.addContenido(tagLeido);
            if (this.dtdValidador != null) {
                this.dtdValidador.validarContenidoAInsertar(tagLeido);
            }
        } catch (DTDException ex) {
            this.clasificarException(new SAXException(SAXException.ERROR, ex.getMessage()));
        } catch (SAXException ex) {
            this.clasificarException(ex);
        }
    }

    private void tagAutoContenido(String tagLeido) {
        String nombreTag = StrOp.getNombreTag(tagLeido, "<", "/>");
        AtributoValor[] variables = this.getVariables(tagLeido, "<", "/>");
        this.equilibrio.add(tagLeido, Clasificador.TAG_AUTOCONTENIDO);

        try {
            this.contentH.tagInicio(nombreTag, tagLeido, variables);
            this.contentH.tagFinal(nombreTag, tagLeido);
            if (this.dtdValidador != null) {
                this.dtdValidador.validarInicioElemento(nombreTag, tagLeido, variables);
                this.dtdValidador.validarFinalElemento(nombreTag, tagLeido);
            }
        } catch (DTDException ex) {
            this.clasificarException(new SAXException(SAXException.ERROR, ex.getMessage()));
        } catch (SAXException ex) {
            this.clasificarException(ex);
        }
    }

    // no tiene DTD
    private void setXMLPrologo(String tagLeido) {
        AtributoValor[] variables = this.getVariables(tagLeido, "<?xml", "?>");

        try {
            this.contentH.prologoXML(variables);
        } catch (SAXException ex) {
            this.clasificarException(ex);
        }
    }

    private void tagInicio(String tagLeido) {
        String nombreTag = StrOp.getNombreTag(tagLeido, "<", ">");
        AtributoValor[] variables = this.getVariables(tagLeido, "<", ">");
        this.equilibrio.add(tagLeido, Clasificador.TAG_INICIO);

        try {
            this.contentH.tagInicio(nombreTag, tagLeido, variables);
            if (this.dtdValidador != null) {
                this.dtdValidador.validarInicioElemento(nombreTag, tagLeido, variables);
            }
        } catch (DTDException ex) {
            this.clasificarException(new SAXException(SAXException.ERROR, ex.getMessage()));
        } catch (SAXException ex) {
            this.clasificarException(ex);
        }
    }

    private void tagFinal(String tagLeido) {
        String nombreTag = StrOp.getNombreTag(tagLeido, "</", ">");
        this.equilibrio.add(tagLeido, Clasificador.TAG_FINAL);

        try {
            this.contentH.tagFinal(nombreTag, tagLeido);
            if (this.dtdValidador != null) {
                this.dtdValidador.validarFinalElemento(nombreTag, tagLeido);
            }
        } catch (DTDException ex) {
            this.clasificarException(new SAXException(SAXException.ERROR, ex.getMessage()));
        } catch (SAXException ex) {
            this.clasificarException(ex);
        }
    }

    /*
     * metodos para el Error handler
     */
    private void clasificarException(SAXException ex) {
        int tipoException = ex.getTipo();

        if (errorH != null) {
            if (tipoException == SAXException.ERROR) {
                this.error(ex);
            }
            else if (tipoException == SAXException.ERROR_FATAL) {
                this.errorFatal(ex);
            }
            else if (tipoException == SAXException.WARNING) {
                this.warning(ex);
            }
        }
    }

    private void errorFatal(SAXException ex) {
        try {
            errorH.errorFatal(ex);
        } catch (SAXException ex1) {
            this.clasificarException(ex1);
        }
    }

    private void error(SAXException ex) {
        try {
            errorH.error(ex);
        } catch (SAXException ex1) {
            this.clasificarException(ex1);
        }
    }

    private void warning(SAXException ex) {
        try {
            errorH.warning(ex);
        } catch (SAXException ex1) {
            this.clasificarException(ex1);
        }
    }

    private AtributoValor[] getVariables(String tagLeido, String tInicio, String tFinal) {
        String[] array = StrOp.separar(tagLeido, " ", tInicio, tFinal, '"', '"');
        Vector<AtributoValor> variables = new Vector<AtributoValor>();

        for (int i = 0; i < array.length; i++) {
            String aux = array[i];
            if (aux.indexOf('=') != -1) {
                int idxEquals = aux.indexOf('=');
                String nombre = aux.substring(0, idxEquals);
                String valor = aux.substring(idxEquals + 1).replaceAll("\"", "");
                variables.add(new AtributoValor(nombre, valor));
            }
        }

        return variables.toArray(new AtributoValor[variables.size()]);
    }
}
